package com.toolbox.aspect;

import com.google.gson.Gson;
import com.toolbox.annotation.OperateLog;
import com.toolbox.bo.AccountBO;
import com.toolbox.dal.model.InfraLogOperateDO;
import com.toolbox.handler.AccountContext;
import com.toolbox.handler.RequestHandler;
import com.toolbox.service.InfraLogOperateService;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.ip2region.core.Ip2regionSearcher;
import net.dreamlu.mica.ip2region.core.IpInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * <p>
 * Description：
 * </p>
 *
 * @author Mango
 * @version v1.0.0
 * @date 2023-12-07 10:02
 * @see com.toolbox.aspect
 */
@SuppressWarnings("ALL")
@Slf4j
@Aspect
@Component
public class OperateLogAspect implements Ordered {

    /**
     * 操作日志表 服务类
     */
    @Autowired
    private InfraLogOperateService infraLogOperateService;

    /**
     * 核心处理类
     */
    @Autowired
    private Ip2regionSearcher ip2regionSearcher;

    @Pointcut(value = "@annotation(com.toolbox.annotation.OperateLog)")
    public void operateFunc() {

    }

    @Around("operateFunc()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        save(point, time, result);
        return result;
    }

    /**
     * 保存操作日志
     * @param joinPoint
     * @param time
     * @param result
     */
    private void save(ProceedingJoinPoint joinPoint, long time, Object result) {
        // 获取操作人信息
        AccountBO bo = AccountContext.getUser();

        if(bo == null){
            bo = init();
        }


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();

        String ipAddr = RequestHandler.getIpAddr(RequestHandler.getRequest());
        // ip转ip地址归属地
        IpInfo ipInfo = ip2regionSearcher.memorySearch(ipAddr);

        OperateLog annotation = method.getAnnotation(OperateLog.class);

        InfraLogOperateDO operationEntity = new InfraLogOperateDO();

        // 操作名称
        operationEntity.setOperateName(annotation.operateName());

        // 操作对象
        operationEntity.setOperateItem(annotation.operateItem());

        // 请求URL
        operationEntity.setUrl(RequestHandler.getRequestUrl());

        // 请求方式
        operationEntity.setMethod(RequestHandler.getMethod());

        Object[] args = joinPoint.getArgs();
        // 设置请求参数
        if (args.length == 1) {
            // 请求参数
            operationEntity.setRequestData(new Gson().toJson(args[0]));
        } else if (args.length > 1) {
            // 请求参数
            operationEntity.setRequestData(new Gson().toJson(args));
        }

        // 响应参数
        operationEntity.setResponseData(new Gson().toJson(result));

        // IP地址
        operationEntity.setIpAddress(ipAddr);

        // 地区
        if (Objects.nonNull(ipInfo)) {
            // 设置地区
            operationEntity.setRegion(ipInfo.getAddress());
        }

        // 浏览器
        operationEntity.setBrowser(RequestHandler.getBrowser());

        // 操作系统
        operationEntity.setOperatingSystem(RequestHandler.getOperatingSystem());

        // 用户代理
        operationEntity.setAgent(RequestHandler.getUserAgent());

        // 执行时长
        operationEntity.setTimes(time);

        // 完整方法名
        operationEntity.setMethodName(className + "." + methodName + "()");

        operationEntity.setCreatorId(bo.getUserId());
        operationEntity.setCreatorName(bo.getNickname());

        operationEntity.setModifierId(bo.getUserId());
        operationEntity.setModifierName(bo.getNickname());

        infraLogOperateService.create(operationEntity);
    }

    /**
     * 记录操作日志
     *
     * @param joinPoint
     * @param result
     * @throws Throwable
     */
    @AfterReturning(value = "operateFunc()", argNames = "joinPoint,object", returning = "object")
    public void operateLog(JoinPoint joinPoint, Object result) throws Throwable {
        OperateLog annotation = getAnnotation(joinPoint);
    }

    /**
     * 获取注解
     *
     * @param joinPoint 切点
     * @return 注解
     */
    private OperateLog getAnnotation(JoinPoint joinPoint) {
        MethodSignature sign = (MethodSignature) joinPoint.getSignature();
        Method method = sign.getMethod();
        OperateLog annotation = method.getAnnotation(OperateLog.class);

        return annotation;
    }

    private AccountBO init() {
        AccountBO bo = new AccountBO();

        bo.setUserId(1L);
        bo.setNickname("system-root");

        return bo;
    }

    @Override
    public int getOrder() {
        return -1000;
    }

}
